All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glowroot.agent.impl.AggregateIntervalCollector Maven / Gradle / Ivy

There is a newer version: 0.14.0-beta.3
Show newest version
/*
 * Copyright 2015-2018 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.agent.impl;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;

import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.Lists;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.Maps;
import org.glowroot.agent.shaded.org.checkerframework.checker.nullness.qual.Nullable;

import org.glowroot.agent.collector.Collector;
import org.glowroot.agent.collector.Collector.AggregateReader;
import org.glowroot.agent.collector.Collector.AggregateVisitor;
import org.glowroot.agent.model.SharedQueryTextCollection;
import org.glowroot.agent.shaded.org.glowroot.common.live.LiveAggregateRepository.OverviewAggregate;
import org.glowroot.agent.shaded.org.glowroot.common.live.LiveAggregateRepository.PercentileAggregate;
import org.glowroot.agent.shaded.org.glowroot.common.live.LiveAggregateRepository.ThroughputAggregate;
import org.glowroot.agent.shaded.org.glowroot.common.model.LazyHistogram.ScratchBuffer;
import org.glowroot.agent.shaded.org.glowroot.common.model.OverallErrorSummaryCollector;
import org.glowroot.agent.shaded.org.glowroot.common.model.OverallSummaryCollector;
import org.glowroot.agent.shaded.org.glowroot.common.model.ProfileCollector;
import org.glowroot.agent.shaded.org.glowroot.common.model.QueryCollector;
import org.glowroot.agent.shaded.org.glowroot.common.model.ServiceCallCollector;
import org.glowroot.agent.shaded.org.glowroot.common.model.TransactionNameErrorSummaryCollector;
import org.glowroot.agent.shaded.org.glowroot.common.model.TransactionNameSummaryCollector;
import org.glowroot.agent.shaded.org.glowroot.common.util.CaptureTimes;
import org.glowroot.agent.shaded.org.glowroot.common.util.Clock;
import org.glowroot.agent.shaded.org.glowroot.wire.api.model.AggregateOuterClass.Aggregate;

public class AggregateIntervalCollector {

    private static final String LIMIT_EXCEEDED_BUCKET = "LIMIT EXCEEDED BUCKET";

    private final long captureTime;
    private final int maxTransactionAggregates;
    private final int maxQueryAggregates;
    private final int maxServiceCallAggregates;
    private final Clock clock;

    private final ConcurrentMap typeCollectors =
            Maps.newConcurrentMap();

    AggregateIntervalCollector(long currentTime, long aggregateIntervalMillis,
            int maxTransactionAggregates, int maxQueryAggregates, int maxServiceCallAggregates,
            Clock clock) {
        captureTime = CaptureTimes.getRollup(currentTime, aggregateIntervalMillis);
        this.maxTransactionAggregates = maxTransactionAggregates;
        this.maxQueryAggregates = maxQueryAggregates;
        this.maxServiceCallAggregates = maxServiceCallAggregates;
        this.clock = clock;
    }

    public long getCaptureTime() {
        return captureTime;
    }

    public void add(Transaction transaction) {
        IntervalTypeCollector typeCollector = typeCollectors.get(transaction.getTransactionType());
        if (typeCollector == null) {
            // don't need to worry about race condition here because add() is only called from a
            // single thread (TransactionProcessorLoop)
            typeCollector = new IntervalTypeCollector();
            typeCollectors.put(transaction.getTransactionType(), typeCollector);
        }
        typeCollector.add(transaction);
    }

    public void mergeOverallSummaryInto(OverallSummaryCollector collector, String transactionType) {
        IntervalTypeCollector typeCollector = typeCollectors.get(transactionType);
        if (typeCollector == null) {
            return;
        }
        typeCollector.overallAggregateCollector.mergeOverallSummaryInto(collector);
    }

    public void mergeTransactionNameSummariesInto(TransactionNameSummaryCollector collector,
            String transactionType) {
        IntervalTypeCollector typeCollector = typeCollectors.get(transactionType);
        if (typeCollector == null) {
            return;
        }
        for (AggregateCollector aggregateCollector : typeCollector.transactionAggregateCollectors
                .values()) {
            aggregateCollector.mergeTransactionNameSummariesInto(collector);
        }
    }

    public void mergeOverallErrorSummaryInto(OverallErrorSummaryCollector collector,
            String transactionType) {
        IntervalTypeCollector typeCollector = typeCollectors.get(transactionType);
        if (typeCollector == null) {
            return;
        }
        typeCollector.overallAggregateCollector.mergeOverallErrorSummaryInto(collector);
    }

    public void mergeTransactionNameErrorSummariesInto(
            TransactionNameErrorSummaryCollector collector, String transactionType) {
        IntervalTypeCollector typeCollector = typeCollectors.get(transactionType);
        if (typeCollector == null) {
            return;
        }
        for (AggregateCollector aggregateCollector : typeCollector.transactionAggregateCollectors
                .values()) {
            aggregateCollector.mergeTransactionNameErrorSummariesInto(collector);
        }
    }

    public @Nullable OverviewAggregate getOverviewAggregate(String transactionType,
            @Nullable String transactionName) {
        AggregateCollector aggregateCollector =
                getAggregateCollector(transactionType, transactionName);
        if (aggregateCollector == null) {
            return null;
        }
        long liveCaptureTime = Math.min(captureTime, clock.currentTimeMillis());
        return aggregateCollector.getOverviewAggregate(liveCaptureTime);
    }

    public @Nullable PercentileAggregate getPercentileAggregate(String transactionType,
            @Nullable String transactionName) {
        AggregateCollector aggregateCollector =
                getAggregateCollector(transactionType, transactionName);
        if (aggregateCollector == null) {
            return null;
        }
        long liveCaptureTime = Math.min(captureTime, clock.currentTimeMillis());
        return aggregateCollector.getPercentileAggregate(liveCaptureTime);
    }

    public @Nullable ThroughputAggregate getThroughputAggregate(String transactionType,
            @Nullable String transactionName) {
        AggregateCollector aggregateCollector =
                getAggregateCollector(transactionType, transactionName);
        if (aggregateCollector == null) {
            return null;
        }
        long liveCaptureTime = Math.min(captureTime, clock.currentTimeMillis());
        return aggregateCollector.getThroughputAggregate(liveCaptureTime);
    }

    public @Nullable String getFullQueryText(String fullQueryTextSha1) {
        for (IntervalTypeCollector typeCollector : typeCollectors.values()) {
            String fullQueryText = typeCollector.getFullQueryText(fullQueryTextSha1);
            if (fullQueryText != null) {
                return fullQueryText;
            }
        }
        return null;
    }

    public void mergeQueriesInto(QueryCollector collector, String transactionType,
            @Nullable String transactionName) {
        AggregateCollector aggregateCollector =
                getAggregateCollector(transactionType, transactionName);
        if (aggregateCollector == null) {
            return;
        }
        aggregateCollector.mergeQueriesInto(collector);
    }

    public void mergeServiceCallsInto(ServiceCallCollector collector, String transactionType,
            @Nullable String transactionName) {
        AggregateCollector aggregateCollector =
                getAggregateCollector(transactionType, transactionName);
        if (aggregateCollector == null) {
            return;
        }
        aggregateCollector.mergeServiceCallsInto(collector);
    }

    public void mergeMainThreadProfilesInto(ProfileCollector collector, String transactionType,
            @Nullable String transactionName) {
        AggregateCollector aggregateCollector =
                getAggregateCollector(transactionType, transactionName);
        if (aggregateCollector == null) {
            return;
        }
        aggregateCollector.mergeMainThreadProfilesInto(collector);
    }

    public void mergeAuxThreadProfilesInto(ProfileCollector collector, String transactionType,
            @Nullable String transactionName) {
        AggregateCollector aggregateCollector =
                getAggregateCollector(transactionType, transactionName);
        if (aggregateCollector == null) {
            return;
        }
        aggregateCollector.mergeAuxThreadProfilesInto(collector);
    }

    // TODO report checker framework issue that occurs without this suppression
    @SuppressWarnings("return.type.incompatible")
    Set getTransactionTypes() {
        return typeCollectors.keySet();
    }

    void flush(Collector collector) throws Exception {
        collector.collectAggregates(new AggregateReaderImpl(captureTime));
    }

    void clear() {
        typeCollectors.clear();
    }

    // can be called without lock
    private @Nullable AggregateCollector getAggregateCollector(String transactionType,
            @Nullable String transactionName) {
        IntervalTypeCollector intervalTypeCollector = typeCollectors.get(transactionType);
        if (intervalTypeCollector == null) {
            return null;
        }
        if (transactionName == null) {
            return intervalTypeCollector.overallAggregateCollector;
        } else {
            return intervalTypeCollector.transactionAggregateCollectors.get(transactionName);
        }
    }

    private class IntervalTypeCollector {

        private final AggregateCollector overallAggregateCollector;
        private final Map transactionAggregateCollectors =
                Maps.newConcurrentMap();

        private IntervalTypeCollector() {
            overallAggregateCollector =
                    new AggregateCollector(null, maxQueryAggregates, maxServiceCallAggregates);
        }

        private void add(Transaction transaction) {
            merge(transaction, overallAggregateCollector);
            AggregateCollector transactionAggregateCollector =
                    transactionAggregateCollectors.get(transaction.getTransactionName());
            if (transactionAggregateCollector == null) {
                // don't need to worry about race condition here because add() is only called from a
                // single thread (TransactionProcessorLoop)
                if (transactionAggregateCollectors.size() < maxTransactionAggregates) {
                    transactionAggregateCollector =
                            createTransactionAggregateCollector(transaction.getTransactionName());
                } else {
                    transactionAggregateCollector =
                            transactionAggregateCollectors.get(LIMIT_EXCEEDED_BUCKET);
                    if (transactionAggregateCollector == null) {
                        transactionAggregateCollector =
                                createTransactionAggregateCollector(LIMIT_EXCEEDED_BUCKET);
                    }
                }
            }
            merge(transaction, transactionAggregateCollector);
        }

        private AggregateCollector createTransactionAggregateCollector(String transactionName) {
            AggregateCollector transactionAggregateCollector = new AggregateCollector(
                    transactionName, maxQueryAggregates, maxServiceCallAggregates);
            transactionAggregateCollectors.put(transactionName, transactionAggregateCollector);
            return transactionAggregateCollector;
        }

        private void merge(Transaction transaction, AggregateCollector aggregateCollector) {
            aggregateCollector.mergeDataFrom(transaction);
        }

        private @Nullable String getFullQueryText(String fullQueryTextSha1) {
            String fullQueryText = overallAggregateCollector.getFullQueryText(fullQueryTextSha1);
            if (fullQueryText != null) {
                return fullQueryText;
            }
            for (AggregateCollector aggregateCollector : transactionAggregateCollectors.values()) {
                fullQueryText = aggregateCollector.getFullQueryText(fullQueryTextSha1);
                if (fullQueryText != null) {
                    return fullQueryText;
                }
            }
            return null;
        }
    }

    private class AggregateReaderImpl implements AggregateReader {

        private final long captureTime;

        private AggregateReaderImpl(long captureTime) {
            this.captureTime = captureTime;
        }

        @Override
        public long captureTime() {
            return captureTime;
        }

        @Override
        public void accept(AggregateVisitor aggregateVisitor) throws Exception {
            SharedQueryTextCollectionImpl sharedQueryTextCollector =
                    new SharedQueryTextCollectionImpl();
            ScratchBuffer scratchBuffer = new ScratchBuffer();
            for (Map.Entry e : typeCollectors.entrySet()) {
                String transactionType = e.getKey();
                IntervalTypeCollector intervalTypeCollector = e.getValue();
                Aggregate overallAggregate = intervalTypeCollector.overallAggregateCollector
                        .build(sharedQueryTextCollector, scratchBuffer);
                aggregateVisitor.visitOverallAggregate(transactionType,
                        sharedQueryTextCollector.getAndClearLastestSharedQueryTexts(),
                        overallAggregate);
                for (Map.Entry f : intervalTypeCollector.transactionAggregateCollectors
                        .entrySet()) {
                    Aggregate transactionAggregate =
                            f.getValue().build(sharedQueryTextCollector, scratchBuffer);
                    aggregateVisitor.visitTransactionAggregate(transactionType, f.getKey(),
                            sharedQueryTextCollector.getAndClearLastestSharedQueryTexts(),
                            transactionAggregate);
                }
            }
        }
    }

    private static class SharedQueryTextCollectionImpl implements SharedQueryTextCollection {

        private final Map sharedQueryTextIndexes = Maps.newHashMap();

        private List latestSharedQueryTexts = Lists.newArrayList();

        public List getAndClearLastestSharedQueryTexts() {
            List latestSharedQueryTexts = this.latestSharedQueryTexts;
            this.latestSharedQueryTexts = Lists.newArrayList();
            return latestSharedQueryTexts;
        }

        @Override
        public int getSharedQueryTextIndex(String queryText) {
            Integer sharedQueryTextIndex = sharedQueryTextIndexes.get(queryText);
            if (sharedQueryTextIndex == null) {
                sharedQueryTextIndex = sharedQueryTextIndexes.size();
                sharedQueryTextIndexes.put(queryText, sharedQueryTextIndex);
                latestSharedQueryTexts.add(queryText);
            }
            return sharedQueryTextIndex;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy