Java tutorial
/* * Copyright 2014-2019 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.ui; import java.util.List; import com.google.common.base.Function; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.checkerframework.checker.nullness.qual.Nullable; import org.glowroot.common.ConfigDefaults; import org.glowroot.common.live.ImmutableAggregateQuery; import org.glowroot.common.live.ImmutableSummaryQuery; import org.glowroot.common.live.ImmutableThroughputAggregate; import org.glowroot.common.live.LiveAggregateRepository; import org.glowroot.common.live.LiveAggregateRepository.AggregateQuery; import org.glowroot.common.live.LiveAggregateRepository.LiveResult; import org.glowroot.common.live.LiveAggregateRepository.OverviewAggregate; import org.glowroot.common.live.LiveAggregateRepository.PercentileAggregate; import org.glowroot.common.live.LiveAggregateRepository.SummaryQuery; import org.glowroot.common.live.LiveAggregateRepository.ThroughputAggregate; import org.glowroot.common.model.MutableProfile; import org.glowroot.common.model.OverallSummaryCollector; import org.glowroot.common.model.ProfileCollector; import org.glowroot.common.model.QueryCollector; import org.glowroot.common.model.Result; import org.glowroot.common.model.ServiceCallCollector; import org.glowroot.common.model.TransactionNameSummaryCollector; import org.glowroot.common.model.TransactionNameSummaryCollector.SummarySortOrder; import org.glowroot.common.model.TransactionNameSummaryCollector.TransactionNameSummary; import org.glowroot.common.util.CaptureTimes; import org.glowroot.common.util.Clock; import org.glowroot.common2.repo.AggregateRepository; import org.glowroot.common2.repo.ConfigRepository; import org.glowroot.common2.repo.ConfigRepository.AgentConfigNotFoundException; import org.glowroot.common2.repo.MutableAggregate; import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.AdvancedConfig; import org.glowroot.wire.api.model.AggregateOuterClass.Aggregate; import static com.google.common.base.Preconditions.checkNotNull; class TransactionCommonService { private final AggregateRepository aggregateRepository; private final LiveAggregateRepository liveAggregateRepository; private final ConfigRepository configRepository; private final Clock clock; TransactionCommonService(AggregateRepository aggregateRepository, LiveAggregateRepository liveAggregateRepository, ConfigRepository configRepository, Clock clock) { this.aggregateRepository = aggregateRepository; this.liveAggregateRepository = liveAggregateRepository; this.configRepository = configRepository; this.clock = clock; } // query.from() is non-inclusive OverallSummaryCollector readOverallSummary(String agentRollupId, SummaryQuery query, boolean autoRefresh) throws Exception { OverallSummaryCollector collector = new OverallSummaryCollector(); long revisedFrom = query.from(); long revisedTo; if (autoRefresh) { revisedTo = query.to(); } else { revisedTo = liveAggregateRepository.mergeInOverallSummary(agentRollupId, query, collector); } for (int rollupLevel = query.rollupLevel(); rollupLevel >= 0; rollupLevel--) { SummaryQuery revisedQuery = ImmutableSummaryQuery.builder().copyFrom(query).from(revisedFrom) .to(revisedTo).rollupLevel(rollupLevel).build(); aggregateRepository.mergeOverallSummaryInto(agentRollupId, revisedQuery, collector); long lastRolledUpTime = collector.getLastCaptureTime(); revisedFrom = Math.max(revisedFrom, lastRolledUpTime + 1); if (revisedFrom > revisedTo) { break; } } return collector; } // query.from() is non-inclusive Result<TransactionNameSummary> readTransactionNameSummaries(String agentRollupId, SummaryQuery query, SummarySortOrder sortOrder, int limit, boolean autoRefresh) throws Exception { TransactionNameSummaryCollector collector = new TransactionNameSummaryCollector(); long revisedFrom = query.from(); long revisedTo; if (autoRefresh) { revisedTo = query.to(); } else { revisedTo = liveAggregateRepository.mergeInTransactionNameSummaries(agentRollupId, query, collector); } for (int rollupLevel = query.rollupLevel(); rollupLevel >= 0; rollupLevel--) { SummaryQuery revisedQuery = ImmutableSummaryQuery.builder().copyFrom(query).from(revisedFrom) .to(revisedTo).rollupLevel(rollupLevel).build(); aggregateRepository.mergeTransactionNameSummariesInto(agentRollupId, revisedQuery, sortOrder, limit, collector); long lastRolledUpTime = collector.getLastCaptureTime(); revisedFrom = Math.max(revisedFrom, lastRolledUpTime + 1); if (revisedFrom > revisedTo) { break; } } return collector.getResult(sortOrder, limit); } // query.from() is INCLUSIVE List<OverviewAggregate> getOverviewAggregates(String agentRollupId, AggregateQuery query, boolean autoRefresh) throws Exception { LiveResult<OverviewAggregate> liveResult; long revisedTo; if (autoRefresh) { liveResult = null; revisedTo = query.to(); } else { liveResult = liveAggregateRepository.getOverviewAggregates(agentRollupId, query); revisedTo = liveResult == null ? query.to() : liveResult.revisedTo(); } AggregateQuery revisedQuery = ImmutableAggregateQuery.builder().copyFrom(query).to(revisedTo).build(); List<OverviewAggregate> aggregates = aggregateRepository.readOverviewAggregates(agentRollupId, revisedQuery); if (revisedQuery.rollupLevel() == 0) { if (liveResult != null) { aggregates = Lists.newArrayList(aggregates); aggregates.addAll(liveResult.get()); } return aggregates; } long nonRolledUpFrom = revisedQuery.from(); if (!aggregates.isEmpty()) { nonRolledUpFrom = Iterables.getLast(aggregates).captureTime() + 1; } List<OverviewAggregate> orderedNonRolledUpAggregates = Lists.newArrayList(); if (nonRolledUpFrom <= revisedTo) { orderedNonRolledUpAggregates .addAll(aggregateRepository.readOverviewAggregates(agentRollupId, ImmutableAggregateQuery .builder().copyFrom(revisedQuery).from(nonRolledUpFrom).rollupLevel(0).build())); } if (liveResult != null) { orderedNonRolledUpAggregates.addAll(liveResult.get()); } aggregates = Lists.newArrayList(aggregates); long fixedIntervalMillis = configRepository.getRollupConfigs().get(revisedQuery.rollupLevel()) .intervalMillis(); aggregates.addAll(rollUpOverviewAggregates(orderedNonRolledUpAggregates, new RollupCaptureTimeFn(fixedIntervalMillis))); if (aggregates.size() >= 2) { long currentTime = clock.currentTimeMillis(); OverviewAggregate nextToLastAggregate = aggregates.get(aggregates.size() - 2); if (currentTime - nextToLastAggregate.captureTime() < 60000) { aggregates.remove(aggregates.size() - 1); } } return aggregates; } // query.from() is INCLUSIVE List<PercentileAggregate> getPercentileAggregates(String agentRollupId, AggregateQuery query, boolean autoRefresh) throws Exception { LiveResult<PercentileAggregate> liveResult; long revisedTo; if (autoRefresh) { liveResult = null; revisedTo = query.to(); } else { liveResult = liveAggregateRepository.getPercentileAggregates(agentRollupId, query); revisedTo = liveResult == null ? query.to() : liveResult.revisedTo(); } AggregateQuery revisedQuery = ImmutableAggregateQuery.builder().copyFrom(query).to(revisedTo).build(); List<PercentileAggregate> aggregates = aggregateRepository.readPercentileAggregates(agentRollupId, revisedQuery); if (revisedQuery.rollupLevel() == 0) { if (liveResult != null) { aggregates = Lists.newArrayList(aggregates); aggregates.addAll(liveResult.get()); } return aggregates; } long nonRolledUpFrom = revisedQuery.from(); if (!aggregates.isEmpty()) { nonRolledUpFrom = Iterables.getLast(aggregates).captureTime() + 1; } List<PercentileAggregate> orderedNonRolledUpAggregates = Lists.newArrayList(); if (nonRolledUpFrom <= revisedTo) { orderedNonRolledUpAggregates .addAll(aggregateRepository.readPercentileAggregates(agentRollupId, ImmutableAggregateQuery .builder().copyFrom(revisedQuery).from(nonRolledUpFrom).rollupLevel(0).build())); } if (liveResult != null) { orderedNonRolledUpAggregates.addAll(liveResult.get()); } aggregates = Lists.newArrayList(aggregates); long fixedIntervalMillis = configRepository.getRollupConfigs().get(revisedQuery.rollupLevel()) .intervalMillis(); aggregates.addAll(rollUpPercentileAggregates(orderedNonRolledUpAggregates, new RollupCaptureTimeFn(fixedIntervalMillis))); if (aggregates.size() >= 2) { long currentTime = clock.currentTimeMillis(); PercentileAggregate nextToLastAggregate = aggregates.get(aggregates.size() - 2); if (currentTime - nextToLastAggregate.captureTime() < 60000) { aggregates.remove(aggregates.size() - 1); } } return aggregates; } // query.from() is INCLUSIVE List<ThroughputAggregate> getThroughputAggregates(String agentRollupId, AggregateQuery query, boolean autoRefresh) throws Exception { LiveResult<ThroughputAggregate> liveResult; long revisedTo; if (autoRefresh) { liveResult = null; revisedTo = query.to(); } else { liveResult = liveAggregateRepository.getThroughputAggregates(agentRollupId, query); revisedTo = liveResult == null ? query.to() : liveResult.revisedTo(); } AggregateQuery revisedQuery = ImmutableAggregateQuery.builder().copyFrom(query).to(revisedTo).build(); List<ThroughputAggregate> aggregates = aggregateRepository.readThroughputAggregates(agentRollupId, revisedQuery); if (revisedQuery.rollupLevel() == 0) { if (liveResult != null) { aggregates = Lists.newArrayList(aggregates); aggregates.addAll(liveResult.get()); } return aggregates; } long nonRolledUpFrom = revisedQuery.from(); if (!aggregates.isEmpty()) { nonRolledUpFrom = Iterables.getLast(aggregates).captureTime() + 1; } List<ThroughputAggregate> orderedNonRolledUpAggregates = Lists.newArrayList(); if (nonRolledUpFrom <= revisedTo) { orderedNonRolledUpAggregates .addAll(aggregateRepository.readThroughputAggregates(agentRollupId, ImmutableAggregateQuery .builder().copyFrom(revisedQuery).from(nonRolledUpFrom).rollupLevel(0).build())); } if (liveResult != null) { orderedNonRolledUpAggregates.addAll(liveResult.get()); } aggregates = Lists.newArrayList(aggregates); long fixedIntervalMillis = configRepository.getRollupConfigs().get(revisedQuery.rollupLevel()) .intervalMillis(); aggregates.addAll(rollUpThroughputAggregates(orderedNonRolledUpAggregates, new RollupCaptureTimeFn(fixedIntervalMillis))); if (aggregates.size() >= 2) { long currentTime = clock.currentTimeMillis(); ThroughputAggregate nextToLastAggregate = aggregates.get(aggregates.size() - 2); if (currentTime - nextToLastAggregate.captureTime() < 60000) { aggregates.remove(aggregates.size() - 1); } } return aggregates; } // query.from() is non-inclusive QueryCollector getMergedQueries(String agentRollupId, AggregateQuery query) throws Exception { int maxQueryAggregatesPerTransactionAggregate = getMaxQueryAggregatesPerTransactionAggregate(agentRollupId); QueryCollector queryCollector = new QueryCollector(maxQueryAggregatesPerTransactionAggregate); long revisedFrom = query.from(); long revisedTo = liveAggregateRepository.mergeInQueries(agentRollupId, query, queryCollector); for (int rollupLevel = query.rollupLevel(); rollupLevel >= 0; rollupLevel--) { AggregateQuery revisedQuery = ImmutableAggregateQuery.builder().copyFrom(query).from(revisedFrom) .to(revisedTo).rollupLevel(rollupLevel).build(); aggregateRepository.mergeQueriesInto(agentRollupId, revisedQuery, queryCollector); long lastRolledUpTime = queryCollector.getLastCaptureTime(); revisedFrom = Math.max(revisedFrom, lastRolledUpTime + 1); if (revisedFrom > revisedTo) { break; } } return queryCollector; } // query.from() is non-inclusive ServiceCallCollector getMergedServiceCalls(String agentRollupId, AggregateQuery query) throws Exception { int maxServiceCallAggregatesPerTransactionAggregate = getMaxServiceCallAggregatesPerTransactionAggregate( agentRollupId); ServiceCallCollector serviceCallCollector = new ServiceCallCollector( maxServiceCallAggregatesPerTransactionAggregate); long revisedFrom = query.from(); long revisedTo = liveAggregateRepository.mergeInServiceCalls(agentRollupId, query, serviceCallCollector); for (int rollupLevel = query.rollupLevel(); rollupLevel >= 0; rollupLevel--) { AggregateQuery revisedQuery = ImmutableAggregateQuery.builder().copyFrom(query).from(revisedFrom) .to(revisedTo).rollupLevel(rollupLevel).build(); aggregateRepository.mergeServiceCallsInto(agentRollupId, revisedQuery, serviceCallCollector); long lastRolledUpTime = serviceCallCollector.getLastCaptureTime(); revisedFrom = Math.max(revisedFrom, lastRolledUpTime + 1); if (revisedFrom > revisedTo) { break; } } return serviceCallCollector; } // query.from() is non-inclusive ProfileCollector getMergedProfile(String agentRollupId, AggregateQuery query, boolean auxiliary, List<String> includes, List<String> excludes, double truncateBranchPercentage) throws Exception { ProfileCollector profileCollector = getMergedProfile(agentRollupId, query, auxiliary); MutableProfile profile = profileCollector.getProfile(); if (!includes.isEmpty() || !excludes.isEmpty()) { profile.filter(includes, excludes); } profile.truncateBranches(truncateBranchPercentage); return profileCollector; } @Nullable String readFullQueryText(String agentRollupId, String fullQueryTextSha1) throws Exception { // checking live data is not efficient since must perform many sha1 hashes // so check repository first String fullQueryText = aggregateRepository.readFullQueryText(agentRollupId, fullQueryTextSha1); if (fullQueryText != null) { return fullQueryText; } return liveAggregateRepository.getFullQueryText(agentRollupId, fullQueryTextSha1); } boolean hasMainThreadProfile(String agentRollupId, AggregateQuery query) throws Exception { for (int rollupLevel = query.rollupLevel(); rollupLevel >= 0; rollupLevel--) { AggregateQuery revisedQuery = ImmutableAggregateQuery.builder().copyFrom(query).rollupLevel(rollupLevel) .build(); if (aggregateRepository.hasMainThreadProfile(agentRollupId, revisedQuery)) { return true; } } return false; } boolean hasAuxThreadProfile(String agentRollupId, AggregateQuery query) throws Exception { for (int rollupLevel = query.rollupLevel(); rollupLevel >= 0; rollupLevel--) { AggregateQuery revisedQuery = ImmutableAggregateQuery.builder().copyFrom(query).rollupLevel(rollupLevel) .build(); if (aggregateRepository.hasAuxThreadProfile(agentRollupId, revisedQuery)) { return true; } } return false; } static List<OverviewAggregate> rollUpOverviewAggregates( List<OverviewAggregate> orderedNonRolledUpOverviewAggregates, Function<Long, Long> rollupCaptureTimeFn) { List<OverviewAggregate> rolledUpOverviewAggregates = Lists.newArrayList(); MutableAggregate currMergedAggregate = new MutableAggregate(0, 0); long currRollupCaptureTime = Long.MIN_VALUE; long maxCaptureTime = Long.MIN_VALUE; for (OverviewAggregate nonRolledUpOverviewAggregate : orderedNonRolledUpOverviewAggregates) { maxCaptureTime = nonRolledUpOverviewAggregate.captureTime(); long rollupCaptureTime = rollupCaptureTimeFn.apply(maxCaptureTime); if (rollupCaptureTime != currRollupCaptureTime && !currMergedAggregate.isEmpty()) { rolledUpOverviewAggregates.add(currMergedAggregate.toOverviewAggregate(currRollupCaptureTime)); currMergedAggregate = new MutableAggregate(0, 0); } currRollupCaptureTime = rollupCaptureTime; currMergedAggregate.addTotalDurationNanos(nonRolledUpOverviewAggregate.totalDurationNanos()); currMergedAggregate.addTransactionCount(nonRolledUpOverviewAggregate.transactionCount()); currMergedAggregate.mergeMainThreadRootTimers(nonRolledUpOverviewAggregate.mainThreadRootTimers()); currMergedAggregate.mergeMainThreadStats(nonRolledUpOverviewAggregate.mainThreadStats()); Aggregate.Timer auxThreadRootTimer = nonRolledUpOverviewAggregate.auxThreadRootTimer(); if (auxThreadRootTimer != null) { currMergedAggregate.mergeAuxThreadRootTimer(auxThreadRootTimer); // aux thread stats is non-null when aux thread root timer is non-null currMergedAggregate .mergeAuxThreadStats(checkNotNull(nonRolledUpOverviewAggregate.auxThreadStats())); } currMergedAggregate.mergeAsyncTimers(nonRolledUpOverviewAggregate.asyncTimers()); } if (!currMergedAggregate.isEmpty()) { // roll up final one rolledUpOverviewAggregates.add(currMergedAggregate.toOverviewAggregate(maxCaptureTime)); } return rolledUpOverviewAggregates; } static List<PercentileAggregate> rollUpPercentileAggregates( List<PercentileAggregate> orderedNonRolledUpPercentileAggregates, Function<Long, Long> rollupCaptureTimeFn) { List<PercentileAggregate> rolledUpPercentileAggregates = Lists.newArrayList(); MutableAggregate currMergedAggregate = new MutableAggregate(0, 0); long currRollupCaptureTime = Long.MIN_VALUE; long maxCaptureTime = Long.MIN_VALUE; for (PercentileAggregate nonRolledUpPercentileAggregate : orderedNonRolledUpPercentileAggregates) { maxCaptureTime = nonRolledUpPercentileAggregate.captureTime(); long rollupCaptureTime = rollupCaptureTimeFn.apply(maxCaptureTime); if (rollupCaptureTime != currRollupCaptureTime && !currMergedAggregate.isEmpty()) { rolledUpPercentileAggregates.add(currMergedAggregate.toPercentileAggregate(currRollupCaptureTime)); currMergedAggregate = new MutableAggregate(0, 0); } currRollupCaptureTime = rollupCaptureTime; currMergedAggregate.addTotalDurationNanos(nonRolledUpPercentileAggregate.totalDurationNanos()); currMergedAggregate.addTransactionCount(nonRolledUpPercentileAggregate.transactionCount()); currMergedAggregate .mergeDurationNanosHistogram(nonRolledUpPercentileAggregate.durationNanosHistogram()); } if (!currMergedAggregate.isEmpty()) { // roll up final one rolledUpPercentileAggregates.add(currMergedAggregate.toPercentileAggregate(maxCaptureTime)); } return rolledUpPercentileAggregates; } static List<ThroughputAggregate> rollUpThroughputAggregates( List<ThroughputAggregate> orderedNonRolledUpThroughputAggregates, Function<Long, Long> rollupCaptureTimeFn) { List<ThroughputAggregate> rolledUpThroughputAggregates = Lists.newArrayList(); long currTransactionCount = 0; // error_count is null for data inserted prior to glowroot central 0.9.18 // rolling up any interval with null error_count should result in null error_count boolean hasMissingErrorCount = false; long currErrorCount = 0; long currRollupCaptureTime = Long.MIN_VALUE; long maxCaptureTime = Long.MIN_VALUE; for (ThroughputAggregate nonRolledUpThroughputAggregate : orderedNonRolledUpThroughputAggregates) { maxCaptureTime = nonRolledUpThroughputAggregate.captureTime(); long rollupCaptureTime = rollupCaptureTimeFn.apply(maxCaptureTime); if (rollupCaptureTime != currRollupCaptureTime && currTransactionCount > 0) { rolledUpThroughputAggregates.add(ImmutableThroughputAggregate.builder() .captureTime(currRollupCaptureTime).transactionCount(currTransactionCount) .errorCount(hasMissingErrorCount ? null : currErrorCount).build()); currTransactionCount = 0; hasMissingErrorCount = false; currErrorCount = 0; } currRollupCaptureTime = rollupCaptureTime; currTransactionCount += nonRolledUpThroughputAggregate.transactionCount(); Long errorCount = nonRolledUpThroughputAggregate.errorCount(); if (errorCount == null) { hasMissingErrorCount = true; } else { currErrorCount += errorCount; } } if (currTransactionCount > 0) { // roll up final one rolledUpThroughputAggregates.add(ImmutableThroughputAggregate.builder().captureTime(maxCaptureTime) .transactionCount(currTransactionCount).errorCount(hasMissingErrorCount ? null : currErrorCount) .build()); } return rolledUpThroughputAggregates; } private ProfileCollector getMergedProfile(String agentRollupId, AggregateQuery query, boolean auxiliary) throws Exception { ProfileCollector profileCollector = new ProfileCollector(); long revisedFrom = query.from(); long revisedTo; if (auxiliary) { revisedTo = liveAggregateRepository.mergeInAuxThreadProfiles(agentRollupId, query, profileCollector); } else { revisedTo = liveAggregateRepository.mergeInMainThreadProfiles(agentRollupId, query, profileCollector); } for (int rollupLevel = query.rollupLevel(); rollupLevel >= 0; rollupLevel--) { AggregateQuery revisedQuery = ImmutableAggregateQuery.builder().copyFrom(query).from(revisedFrom) .to(revisedTo).rollupLevel(rollupLevel).build(); if (auxiliary) { aggregateRepository.mergeAuxThreadProfilesInto(agentRollupId, revisedQuery, profileCollector); } else { aggregateRepository.mergeMainThreadProfilesInto(agentRollupId, revisedQuery, profileCollector); } long lastRolledUpTime = profileCollector.getLastCaptureTime(); revisedFrom = Math.max(revisedFrom, lastRolledUpTime + 1); if (revisedFrom > revisedTo) { break; } } return profileCollector; } private int getMaxQueryAggregatesPerTransactionAggregate(String agentRollupId) throws Exception { AdvancedConfig advancedConfig; try { advancedConfig = configRepository.getAdvancedConfig(agentRollupId); } catch (AgentConfigNotFoundException e) { return ConfigDefaults.ADVANCED_MAX_QUERY_AGGREGATES; } if (advancedConfig.hasMaxQueryAggregates()) { return advancedConfig.getMaxQueryAggregates().getValue(); } else { return ConfigDefaults.ADVANCED_MAX_QUERY_AGGREGATES; } } private int getMaxServiceCallAggregatesPerTransactionAggregate(String agentRollupId) throws Exception { AdvancedConfig advancedConfig; try { advancedConfig = configRepository.getAdvancedConfig(agentRollupId); } catch (AgentConfigNotFoundException e) { return ConfigDefaults.ADVANCED_MAX_SERVICE_CALL_AGGREGATES; } if (advancedConfig.hasMaxServiceCallAggregates()) { return advancedConfig.getMaxServiceCallAggregates().getValue(); } else { return ConfigDefaults.ADVANCED_MAX_SERVICE_CALL_AGGREGATES; } } private static class RollupCaptureTimeFn implements Function<Long, Long> { private final long fixedIntervalMillis; private RollupCaptureTimeFn(long fixedIntervalMillis) { this.fixedIntervalMillis = fixedIntervalMillis; } @Override public Long apply(Long captureTime) { return CaptureTimes.getRollup(captureTime, fixedIntervalMillis); } } }