de.otto.mongodb.profiler.op.DefaultOpProfiler.java Source code

Java tutorial

Introduction

Here is the source code for de.otto.mongodb.profiler.op.DefaultOpProfiler.java

Source

/*
 *  Copyright 2013 Robert Gacki <robert.gacki@cgi.com>
 *
 *  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 de.otto.mongodb.profiler.op;

import com.google.common.collect.Lists;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.CommandResult;
import com.mongodb.DB;
import com.mongodb.DBObject;
import de.otto.mongodb.profiler.AbstractAsyncProfiler;
import de.otto.mongodb.profiler.InitializingProfiler;
import de.otto.mongodb.profiler.op.analyze.Analyzer;
import de.otto.mongodb.profiler.util.ProfilerThreadFactory;
import org.joda.time.DateTime;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import static com.google.common.base.Preconditions.checkNotNull;
import static de.otto.mongodb.profiler.util.Async.shutdownAndAwaitTermination;

/**
 * The default implementation of the OpProfiler. Uses a fetcher to fetch operations from the profiling collection and
 * add them to a queue for processing. Fetching and processing is asynchronous.
 *
 * @author Robert Gacki
 */
public class DefaultOpProfiler extends AbstractAsyncProfiler implements OpProfiler, InitializingProfiler {

    public static final long DEFAULT_FETCHER_DELAY = 5L;
    public static final long DEFAULT_FETCHER_INITIAL_DELAY = 0L;
    public static final TimeUnit DEFAULT_FETCHER_TIMEUNIT = TimeUnit.SECONDS;

    public static final long DEFAULT_ANALYZER_DELAY = 5L;
    public static final long DEFAULT_ANALYZER_INITIAL_DELAY = 2L;
    public static final TimeUnit DEFAULT_ANALYZER_TIMEUNIT = TimeUnit.SECONDS;

    private final ScheduledExecutorService fetcherExecutorService;
    private final ScheduledExecutorService analyzerExecutorService;
    private final DB db;

    private final Queue<DBObject> rawDataQueue;
    private final Map<String, OpProfile> profiles;
    private final AtomicReference<DateTime> explanationResetMark;

    private final OpProfileDataFetcher opProfileDataFetcher;

    private final AtomicReference<OpProfile.OutlierConfiguration> outlierConfiguration;

    private final Collection<? extends Analyzer> analyzers;

    private volatile boolean shutdown = false;

    public DefaultOpProfiler(final boolean execute, final DB db, final Collection<? extends Analyzer> analyzers,
            final ThreadGroup threadGroup) {

        this(execute, db, analyzers,
                Executors.newSingleThreadScheduledExecutor(
                        new ProfilerThreadFactory(OpProfiler.class, "Fetcher", threadGroup)),
                Executors.newSingleThreadScheduledExecutor(
                        new ProfilerThreadFactory(OpProfiler.class, "Analyzer", threadGroup)));
    }

    protected DefaultOpProfiler(final boolean execute, final DB db, final Collection<? extends Analyzer> analyzers,
            final ScheduledExecutorService fetcherService, final ScheduledExecutorService analyzerService) {

        super(execute);

        this.db = checkNotNull(db);
        this.analyzers = new ArrayList<>(checkNotNull(analyzers));
        this.rawDataQueue = new LinkedTransferQueue<>();
        this.fetcherExecutorService = checkNotNull(fetcherService);
        this.analyzerExecutorService = checkNotNull(analyzerService);
        this.profiles = Collections.synchronizedMap(new HashMap<String, OpProfile>());
        this.explanationResetMark = new AtomicReference<>(DateTime.now());
        this.opProfileDataFetcher = new OpProfileDataFetcher(getExecutionGuard(), db, rawDataQueue);
        this.outlierConfiguration = new AtomicReference<>();
    }

    @Override
    public synchronized void reset() {
        rawDataQueue.clear();
        profiles.clear();
        explainNextQueries();
    }

    @Override
    public synchronized boolean continueProfiling() {
        if (shutdown) {
            return false;
        }
        return super.continueProfiling();
    }

    @Override
    public synchronized DateTime continueProfiling(long duration, TimeUnit timeUnit) {
        if (shutdown) {
            return null;
        }
        return super.continueProfiling(duration, timeUnit);
    }

    @Override
    public ProfilingLevel getProfilingLevel() {
        final CommandResult result = db.command(BasicDBObjectBuilder.start("profile", -1).get());
        result.throwOnError();
        return ProfilingLevel.forValue(result.getInt("was", Integer.MIN_VALUE));
    }

    @Override
    public void setProfilingLevel(ProfilingLevel level) {
        db.command(BasicDBObjectBuilder.start("profile", level.value).get()).throwOnError();
    }

    @Override
    public Collection<QueryProfile> getQueryProfiles() {

        final ArrayList<QueryProfile> queries = new ArrayList<>();
        for (OpProfile profile : profiles.values()) {
            if (profile instanceof QueryProfile) {
                queries.add(((QueryProfile) profile));
            }
        }
        return queries;
    }

    @Override
    public QueryProfile getQueryProfile(String id) {

        final OpProfile profile = profiles.get(id);
        return profile != null && profile instanceof QueryProfile ? ((QueryProfile) profile) : null;
    }

    @Override
    public Collection<? extends OpProfile> getProfiles() {
        return Lists.newArrayList(profiles.values());
    }

    @Override
    public OpProfile getProfile(String id) {
        return profiles.get(id);
    }

    @Override
    public void explainNextQueries() {
        explanationResetMark.set(DateTime.now());
    }

    @Override
    public synchronized void initialize() {

        if (shutdown) {
            throw new IllegalStateException("Profiler is shut down!");
        }

        fetcherExecutorService.scheduleWithFixedDelay(opProfileDataFetcher, DEFAULT_FETCHER_INITIAL_DELAY,
                DEFAULT_FETCHER_DELAY, DEFAULT_FETCHER_TIMEUNIT);

        final Runnable profileAnalyzer = new OpProfileAnalyzer(db, rawDataQueue, profiles, explanationResetMark,
                outlierConfiguration, analyzers);
        analyzerExecutorService.scheduleWithFixedDelay(profileAnalyzer, DEFAULT_ANALYZER_INITIAL_DELAY,
                DEFAULT_ANALYZER_DELAY, DEFAULT_ANALYZER_TIMEUNIT);
    }

    @Override
    public synchronized void shutdown() {

        if (shutdown) {
            return;
        }

        shutdown = true;

        stopProfiling();

        try {
            // Try to close the cursor
            opProfileDataFetcher.close();
        } catch (Exception e) {
            // ignore
        }

        try {
            shutdownAndAwaitTermination(fetcherExecutorService, 60, TimeUnit.SECONDS);
        } finally {
            try {
                shutdownAndAwaitTermination(analyzerExecutorService, 60, TimeUnit.SECONDS);
            } finally {
                // Close cursor again in case it has been reopened
                opProfileDataFetcher.close();
            }
        }
    }

    @Override
    public OpProfile.OutlierConfiguration getOutlierConfiguration() {
        return outlierConfiguration.get();
    }

    @Override
    public void setOutlierConfiguration(OpProfile.OutlierConfiguration configuration) {
        this.outlierConfiguration.set(configuration);
    }
}